/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-06
 * V4.0
 */
package com.jphenix.servlet.multipart.instancea;

import com.jphenix.share.tools.HttpCall;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.standard.docs.ClassInfo;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

/**
 * 下载文件处理类
 * com.jphenix.service.multipart.instancea.DownloadFile
 * 
 * 2019-01-02 增加了一些常用的下载文件方法
 * 2020-07-15 增加了不用传入文件大小即可实现文件下载的方法
 * 
 * @author 刘虻
 * 2007-11-24下午03:16:19
 */
@ClassInfo({"2020-07-15 12:55","下载文件处理类"})
public class DownloadFile {

	protected int bufferSize = 1024; //缓存大小
	protected String basePath = null;//保存根路径
	protected HttpServletResponse response = null; //页面反馈
	protected ServletOutputStream os = null; //输出流
	
	/**
	 * 构造函数
	 * 2007-11-24下午03:16:19
	 */
	public DownloadFile(HttpServletResponse resp,String basePath) {
		super();
		this.response = resp;
		this.basePath = basePath;
	}
	
	
	/**
	 * 获取缓存大小
	 * @author 刘虻
	 * 2007-11-24下午03:32:20
	 * @return 缓存大小
	 */
	public int getBufferSize() {
		return bufferSize;
	}

	/**
	 * 设置缓存大小
	 * @author 刘虻
	 * 2007-11-24下午03:32:32
	 * @param bufferSize 缓存大小
	 */
	public void setBufferSize(int bufferSize) {
		this.bufferSize = bufferSize;
	}



	
	/**
	 * 从文件路径中获取文件名
	 * @author 刘虻
	 * 2007-11-24下午03:37:45
	 * @param filePath 文件路径
	 * @return 文件名
	 */
    protected String getFileName(String filePath) {
		//整理路径
		filePath = filePath.replaceAll("\\\\","/");
		//获取拆分点
		int point = filePath.lastIndexOf('/');
		if(point>-1){
		    return filePath.substring(point+1);
		}
		return filePath;
    }
    
    /**
     * 执行下载
     * @param filePath 文件全路径
     * @throws Exception 异常
     * 2014年8月25日
     * @author 马宝刚
     */
    public void downloadFile(String filePath) throws Exception {
    	downloadFile(filePath,null,null,false,false);
    }
    
    /**
     * 下载文件
     * @param file 文件对象
     * @throws Exception 异常
     * 2019年1月2日
     * @author MBG
     */
    public void download(File file) throws Exception {
    	downloadFile(file.getPath(),null,null,false,false);
    }
    
    /**
     * 下载文件
     * @param filePath   文件绝对路径
     * @throws Exception 异常
     * 2019年1月2日
     * @author MBG
     */
    public void download(String filePath) throws Exception {
    	downloadFile(filePath,null,null,false,false);
    }
    
    /**
     * 下载文件
     * @param objFileName   下载后的文件名
     * @param filePath      文件绝对路径
     * @throws Exception    异常
     * 2019年1月2日
     * @author MBG
     */
    public void download(String objFileName,String filePath) throws Exception {
    	downloadFile(filePath,objFileName,null,false,false);
    }
    
    /**
     * 下载文件
     * @param objFileName   下载后的文件名
     * @param file          需要下载的文件对象
     * @throws Exception    异常
     * 2019年1月2日
     * @author MBG
     */
    public void download(String objFileName,File file) throws Exception {
    	downloadFile(file.getPath(),objFileName,null,false,false);
    }
    
    /**
     * 在页面中显示文件
     * @param filePath    文件绝对路径
     * @throws Exception  异常
     * 2019年1月2日
     * @author MBG
     */
    public void inPage(String filePath) throws Exception {
    	downloadFile(filePath,null,null,true,false);
    }
    
    /**
     * 在页面中显示文件
     * @param file        文件对象
     * @throws Exception  异常
     * 2019年1月2日
     * @author MBG
     */
    public void inPage(File file) throws Exception {
    	downloadFile(file.getPath(),null,null,true,false);
    }
    
	/**
	 * 执行下载文件
	 * @author 刘虻
	 * 2007-11-24下午03:18:42
	 * @param filePath 文件相对路径
	 * @param objFileName 下载文件名
	 * @param contentType 内容类型
	 * @param inPage 是否尝试在浏览器中打开
	 * @throws Exception 执行发生异常
	 */
	public void downloadFile(
			String filePath
			,String objFileName
			,String contentType
			,boolean inPage) throws Exception {
		downloadFile(filePath,objFileName,contentType,inPage,false);
	}
	
	/**
	 * 获取输出流
	 * @return 输出流
	 * @throws Exception 异常
	 * 2014年8月29日
	 * @author 马宝刚
	 */
	protected ServletOutputStream getOutputStream() throws Exception {
		if(os==null) {
			os = response.getOutputStream();
		}
		return os;
	}
	
	/**
	 * 写入文件内容
	 * @param bytes			文件内容
	 * @throws Exception	异常
	 * 2014年8月29日
	 * @author 马宝刚
	 */
	public void write(byte[] bytes) throws Exception {
		getOutputStream().write(bytes);
	}
	
	/**
	 * 关闭流
	 * @throws Exception 异常
	 * 2014年8月29日
	 * @author 马宝刚
	 */
	public void close() throws Exception {
		try {
			os.flush();
		}catch(Exception e) {}
		try {
			os.close();
		}catch(Exception e) {}
		os = null;
	}
	
	/**
	 * 执行此方法设置输出文件报文头，随后直接使用
	 * @param objFileName 下载文件名
	 * @param contentType 内容类型
	 * @param inPage 是否在浏览器中打开
	 * @throws Exception 异常
	 * 2014年8月29日
	 * @author 马宝刚
	 */
	public void beginDownload(String objFileName,String contentType,boolean inPage) throws Exception {
        //重置一下缓存否则WebLogic会报错
        try {
        	response.resetBuffer();
        }catch(Exception e) {}
        
        try {
	        //需要转换成ISO-8859-1 避免汉字乱码
	        response.setHeader(
	        		"Content-Disposition"
	        		,(inPage?"inline":"attachment")+";filename=\""
	        			+(new String(objFileName.getBytes(), StandardCharsets.ISO_8859_1))+"\"");
	        //设置下载类型
	        if(contentType==null) {
	        	contentType = "application/x-msdownload";
	        }
	        response.setContentType(contentType);
        }catch(Exception e) {
    		//忽略客户端放弃下载异常
    		return;
        }
    }
	
	/**
	 * 设置文件根路径 简称  basePath()
	 * @param basePath 文件根路径
	 * 2016年11月14日
	 * @author MBG
	 */
	public void setBasePath(String basePath) {
		if(basePath==null || basePath.trim().length()<1) {
			return;
		}
		this.basePath = basePath;
	}
	
	/**
	 * 设置文件根路径 全称： setBasePath
	 * @param basePath 文件根路径
	 * 2016年11月14日
	 * @author MBG
	 */
	public void basePath(String basePath) {
		if(basePath==null || basePath.trim().length()<1) {
			return;
		}
		this.basePath = basePath;
	}
	
	/**
	 * 获取文件根路径 简称：basePath
	 * @return 文件根路径
	 * 2016年11月14日
	 * @author MBG
	 */
	public String getBasePath() {
		return basePath==null?"":basePath;
	}

	
	/**
	 * 获取文件根路径 全称：getBasePath
	 * @return 文件根路径
	 * 2016年11月14日
	 * @author MBG
	 */
	public String basePath() {
		return basePath==null?"":basePath;
	}
	
	
	
	/**
	 * 输出文件到页面（通过文件路径自动判断文件类型）
	 * @param fullFilePath 文件全路径
	 * @param objFileName  下载时的文件名（没经过转码，需要手动转码）
	 * 通常为：  new String(objFileName.getBytes(),"ISO-8859-1")
	 * 但是苹果显示乱码。苹果格式：  new String(objFileName.getBytes("UTF-8"),"ISO-8859-1")
	 * @param inPage 是否在页面中显示
	 * @throws Exception 异常
	 * 2016年11月17日
	 * @author MBG
	 */
	public void outFile(
			String fullFilePath
			,String objFileName
			,boolean inPage) throws Exception {
		if (fullFilePath==null) {
			throw new Exception("Target File Path Is Null");
		}
		//文件扩展名
		String extName = getFileExtName(fullFilePath);
		
		//下载文件类型信息
		String mame = getMime(extName);
		
		//输出文件到页面
		outFile(fullFilePath,objFileName,mame,inPage);
	}
	
	/**
	 * 输出文件到页面（通过文件路径自动判断文件类型，自动是否显示在页面中）
	 * @param fullFilePath 文件全路径
	 * @param objFileName  下载时的文件名（没经过转码，需要手动转码）
	 * 通常为：  new String(objFileName.getBytes(),"ISO-8859-1")
	 * 但是苹果显示乱码。苹果格式：  new String(objFileName.getBytes("UTF-8"),"ISO-8859-1")
	 * @throws Exception 异常
	 * 2016年11月17日
	 * @author MBG
	 */
	public void outFile(
			String fullFilePath
			,String objFileName) throws Exception {
		if (fullFilePath==null) {
			throw new Exception("Target File Path Is Null");
		}
		//文件扩展名
		String extName = getFileExtName(fullFilePath);
		//下载文件类型信息
		String[] mameInfos = getMimeInfo(extName);
		boolean inPage = false; //是否显示在页面中
		if(mameInfos[1]!=null && "1".equals(mameInfos[1])) {
			inPage = true;
		}
		//输出文件到页面
		outFile(fullFilePath,objFileName,mameInfos[0],inPage);
	}
	
	/**
	 * 输出文件到页面（通过文件路径自动判断文件类型，自动是否显示在页面中）
	 * @param fullFilePath 文件全路径
	 * @throws Exception 异常
	 * 2016年11月17日
	 * @author MBG
	 */
	public void outFile(String fullFilePath) throws Exception {
		if (fullFilePath==null) {
			throw new Exception("Target File Path Is Null");
		}
		//文件扩展名
		String extName = getFileExtName(fullFilePath);
		//下载文件类型信息
		String[] mameInfos = getMimeInfo(extName);
		boolean inPage = false; //是否显示在页面中
		if(mameInfos[1]!=null && "1".equals(mameInfos[1])) {
			inPage = true;
		}
		//输出文件到页面
		outFile(fullFilePath,getFileName(fullFilePath),mameInfos[0],inPage);
	}
	
	
	/**
	 * 输出文件到页面
	 * @param fullFilePath 文件全路径
	 * @param objFileName  下载时的文件名（没经过转码，需要手动转码）
	 * 通常为：  new String(objFileName.getBytes(),"ISO-8859-1")
	 * 但是苹果显示乱码。苹果格式：  new String(objFileName.getBytes("UTF-8"),"ISO-8859-1")
	 * @param contentType 附件类型
	 * @param inPage 是否在页面中显示
	 * @throws Exception 异常
	 * 2016年11月17日
	 * @author MBG
	 */
	public void outFile(
			String fullFilePath
			,String objFileName
			,String contentType
			,boolean inPage) throws Exception {
		if (fullFilePath==null) {
			throw new Exception("Target File Path Is Null");
		}
		//构建下载文件对象
        File downLoadFile = new File(fullFilePath);
        
        if (!downLoadFile.exists()) {
        	throw new Exception("Target File Not Exists:["+downLoadFile.getPath()+"]");
        }
        //重置一下缓存否则WebLogic会报错
        try {
        	response.resetBuffer();
        }catch(Exception e) {}
        
        //构建文件读取流
        FileInputStream fis = null;
        
        try {
	        fis = new FileInputStream(downLoadFile);
	        //缓存数组
	        byte[] bufferBytes = new byte[getBufferSize()];
	        
	        //获取文件大小
	        int fileSize = (int)downLoadFile.length();
	        
	        
	        if (objFileName==null || objFileName.length()==0) {
	        	objFileName = getFileName(fullFilePath);
	        }
	        //需要转换成ISO-8859-1 避免汉字乱码
	        response.setHeader(
	        		"Content-Disposition"
	        		,(inPage?"inline":"attachment")+";filename=\""
	        			+objFileName+"\"");
	        
	        //设置下载类型
	        if(contentType==null) {
	        	contentType = "application/x-msdownload";
	        }
	        response.setContentType(contentType);
	        response.setContentLength(fileSize);
	        int readCount = 0; //读取字节数
	        while(readCount<fileSize){
	        	//获取本次读取字节数
	            int readByteCount = fis.read(bufferBytes,0,getBufferSize());
	            readCount += readByteCount; //累加读取字节
	            //输出页面
	            response.getOutputStream().write(bufferBytes,0,readByteCount);
	        }
        }catch(Exception e) {
    		//忽略客户端放弃下载异常
    		return;
        }finally {
            try {
                fis.close();
            }catch(Exception e) {}
        }
    }
	
	
	
	/**
	 * 执行下载文件
	 * @author 刘虻
	 * 2007-11-24下午03:18:42
	 * @param filePath 文件相对路径
	 * @param objFileName 下载文件名
	 * @param contentType 内容类型
	 * @param inPage 是否尝试在浏览器中打开
	 * @param deleteFile 下载后是否删除
	 * @throws Exception 执行发生异常
	 */
	public void downloadFile(
			String filePath
			,String objFileName
			,String contentType
			,boolean inPage
			,boolean deleteFile) throws Exception {
		
		if (filePath==null) {
			throw new Exception("Target File Path Is Null");
		}
		//构建下载文件对象
        File downLoadFile = new File(SFilesUtil.getAllFilePath(filePath,basePath));
        
        if (!downLoadFile.exists()) {
        	throw new Exception("Target File Not Exists:["+downLoadFile.getPath()+"]");
        }
        //重置一下缓存否则WebLogic会报错
        try {
        	response.resetBuffer();
        }catch(Exception e) {}
        
        //构建文件读取流
        FileInputStream fis = null;
        
        try {
	        fis = new FileInputStream(downLoadFile);
	        //缓存数组
	        byte[] bufferBytes = new byte[getBufferSize()];
	        
	        //获取文件大小
	        int fileSize = (int)downLoadFile.length();
	        
	        
	        if (objFileName==null || objFileName.length()==0) {
	        	objFileName = getFileName(filePath);
	        }
	        //需要转换成ISO-8859-1 避免汉字乱码
	        response.setHeader(
	        		"Content-Disposition"
	        		,(inPage?"inline":"attachment")+";filename=\""
	        			+(new String(objFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1))+"\"");
	        //设置下载类型
	        if(contentType==null) {
	        	contentType = getMime(SFilesUtil.getFileExtName(filePath));
	        }
	        response.setContentType(contentType);
	        response.setContentLength(fileSize);
	        int readCount = 0; //读取字节数
	        while(readCount<fileSize){
	        	
	        	//获取本次读取字节数
	            int readByteCount = fis.read(bufferBytes,0,getBufferSize());
	            readCount += readByteCount; //累加读取字节
	            //输出页面
	            response.getOutputStream().write(bufferBytes,0,readByteCount);
	        }
        }catch(Exception e) {
    		//忽略客户端放弃下载异常
    		return;
        }finally {
            try {
                fis.close();
            }catch(Exception e) {}
        	 if(deleteFile) {
        		 try {
        			 downLoadFile.delete();
        		 }catch(Exception e2) {}
        	 }
        }
    }
	
	
	/**
	 * 执行下载文件
	 * @param is          读入流
	 * @param objFileName 目标文件名
	 * @param inPage      是否在浏览器内部打开
	 * @throws Exception  异常
	 * 2017年4月7日
	 * @author MBG
	 */
	public void downloadFile(
			 InputStream is
			,String objFileName
			,boolean inPage) throws Exception {
		downloadFile(is,0,objFileName,inPage);
	}

	
	/**
	 * 执行下载文件
	 * @param is          读入流
	 * @param fileSize    文件大小
	 * @param objFileName 目标文件名
	 * @param inPage      是否在浏览器内部打开
	 * @throws Exception  异常
	 * 2017年4月7日
	 * @author MBG
	 */
	public void downloadFile(
			 InputStream is
			,int fileSize
			,String objFileName
			,boolean inPage) throws Exception {
		if (is==null) {
			throw new Exception("Target File Path Is Null");
		}
        //重置一下缓存否则WebLogic会报错
        try {
        	response.resetBuffer();
        }catch(Exception e) {}
        
        OutputStream os = null; //输出流
        try {
        	os = response.getOutputStream();
	        //缓存数组
	        byte[] bufferBytes = new byte[getBufferSize()];
	        
	        //需要转换成ISO-8859-1 避免汉字乱码
	        response.setHeader(
	        		"Content-Disposition"
	        		,(inPage?"inline":"attachment")+";filename=\""
	        			+(new String(objFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1))+"\"");
	        //设置下载类型
	        String contentType = getMime(SFilesUtil.getFileExtName(objFileName));
	        response.setContentType(contentType);
	        int readByteCount; //本次读取字节数
	        if(fileSize>0) {
		        response.setContentLength(fileSize);
		        int readCount = 0; //读取字节数
		        while(readCount<fileSize){
		        	//获取本次读取字节数
		            readByteCount = is.read(bufferBytes,0,getBufferSize());
		            readCount += readByteCount; //累加读取字节
		            //输出页面
		            os.write(bufferBytes,0,readByteCount);
		        }
	        }else {
				//返回信息中不包含文件长度
				while ((readByteCount=is.read(bufferBytes))!=-1) {
					//输出页面
					os.write(bufferBytes,0,readByteCount);
				}
	        }
        }catch(Exception e) {
    		//忽略客户端放弃下载异常
    		return;
        }finally {
            try {
                is.close();
            }catch(Exception e) {}
			try {
				os.flush();
			}catch(Exception e) {}
			try {
				os.close();
			}catch(Exception e) {}
        }
    }
	
    /**
     * 获取文件路径中,文件的扩展名
     * @author 刘虻
     * 2008-1-18下午12:32:38
     * @param filePathStr 文件路径
     * @return 文件的扩展名
     */
    public String getFileExtName(String filePathStr) {
        //获取文件名
        String fileName = getFileName(filePathStr);
        //获取扩展名分隔符
        int point  = fileName.lastIndexOf(".");
        
        if (point<0) {
            return "";
        }
        return fileName.substring(point+1);
    }
    
	
	
	/**
	 * 通过扩展名，获取文件类型 
	 * @param extName 扩展名
	 * @return 文件类型 
	 * 2016年11月17日
	 * @author MBG
	 */
	public String getMime(String extName){
		return HttpCall.getMime(extName);
	}
	
	/**
	 * 通过扩展名，获取文件类型 0文件类型  1是否可以直接打开
	 * @param extName 扩展名
	 * @return 文件类型  1是否可以直接打开
	 * 2016年11月17日
	 * @author MBG
	 */
	public String[] getMimeInfo(String extName){
		return HttpCall.getMimeInfo(extName);
	}
}
